package com.atXiao.food.aspect;

import com.alibaba.fastjson.JSONObject;
import com.atXiao.food.aspect.annotation.LogAspect;
import com.atXiao.food.config.Log;
import com.atXiao.food.rspbody.RspBody;
import com.atXiao.food.rspcode.RspResultCode;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * 日志AOP-打印请求、响应耗时
 *
 * @author lixiaoxiao
 * @date 2023-11-23
 */
@Aspect
@Component
public class RestLogAspect {
    /**
     * 日志
     */
    private static final Log logger = new Log(RestLogAspect.class);

    /**
     * 开始时间
     */
    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 切面定义，判断类上是否包含注解AopSign或者方法上是否包含LogAspect
     */
    @Pointcut("@annotation(com.atXiao.food.aspect.annotation.LogAspect)")
    public void RestLog() {

    }

    /**
     * 方法执行前，打印入参日志
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("RestLog() && @annotation(logAspect)")
    public void doBefore(JoinPoint joinPoint, LogAspect logAspect) {
        startTime.set(System.currentTimeMillis());
        logger.reqPrint(getTraceId(), logAspect.busSign(), logAspect.busCode(), JSONObject.toJSONString(joinPoint.getArgs()[joinPoint.getArgs().length - 1]));
    }

    /**
     * 方法执行后打印出参以及耗时日志
     *
     * @param joinPoint
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "RestLog()&& @annotation(logAspect)")
    public void doAfterReturning(JoinPoint joinPoint, Object ret, LogAspect logAspect) {
        logger.rspPrint(getTraceId(), logAspect.busSign(), logAspect.busCode(), (System.currentTimeMillis() - startTime.get()), JSONObject.toJSONString(ret));
    }

    /**
     * 异常时打印响应时间
     *
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(throwing = "ex", pointcut = "RestLog()&& @annotation(logAspect)")
    public void doRecoveryActions(JoinPoint joinPoint, LogAspect logAspect, Throwable ex) {
        RspBody resBody = null;
        if (ex instanceof MethodArgumentNotValidException) {
            resBody.setRetCode(RspResultCode.REQUEST_ERROR.getCode());
            resBody.setRetDesc(RspResultCode.REQUEST_ERROR.getDesc());
        } else {
            resBody.setRetCode(RspResultCode.SERVER_FAILURE.getCode());
            resBody.setRetDesc(RspResultCode.SERVER_FAILURE.getDesc());
        }
        logger.rspPrint(getTraceId(), logAspect.busSign(), logAspect.busCode(), (System.currentTimeMillis() - startTime.get()), JSONObject.toJSONString(resBody));
    }

    /**
     * 打印请求响应日志
     *
     * @return
     */
    private String getTraceId() {
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 获取流水号
        String traceId = null;
        if (null != attributes) {
            HttpServletRequest request = attributes.getRequest();
            if (null != request) {
                traceId = request.getHeader("TRACE");
            }
        }
        return traceId;
    }
}
